﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Asol.Reporting.Support.Components;

namespace Asol.Reporting.Editor.Expression
{
    /// <summary>
    /// Okno pro zadávání vzorce
    /// </summary>
    [DefaultEvent("ExpressionSave")]
    public partial class FormulaForm : Asol.Reporting.Support.Components.DForm
    {
        #region Konstrukce, inicializace
        /// <summary>
        /// Konstruktor
        /// </summary>
        public FormulaForm()
        {
            this._Context = new Tests();
            InitializeComponent();
            this.InitComp();
        }
        private void InitComp()
        {
            this._ToolStripInit();
            this._EditorInit();
            this._FunctionListInit();
            this._VariableListInit();
            this._PanelOKInit();
            this._FormulaEditor.Focus();
            this._TestButton.Image = Pics.IconLibrary22.MediaPlaybackStart_FromFile;
            this.Activated += new EventHandler(FormulaForm_Activated);
        }
        void FormulaForm_Activated(object sender, EventArgs e)
        {
            this._FormulaEditor.RtfTextBox.Focus();
        }
        #endregion
        #region Konfigurace okna
        /// <summary>
        /// Uživatelská konfigurace okna. Obsahuje Layout a další data (poslední stav okna).
        /// </summary>
        protected override int[] ConfigUserData
        {
            set
            {   // Provádí se při otevírání okna:
                if (value.Length > 0)
                    this._FunctionListIsCategorized = (value[0] == 2);
                if (value.Length > 1)
                    this._VariableListIsCategorized = (value[1] == 2);
                if (this.UserConfigPersistent == ConfigPersistentMode.Layout && value.Length > 2)
                    this._Split.SplitterPosition = value[2];
                // Změna hodnot _FunctionListIsCategorized a _VariableListIsCategorized přesune Focus do VariableListu, přeneseme jej zpátky do RtfTextu:
                this._FormulaEditor.RtfTextBox.Focus();
            }
            get
            {   // Provádí se při zavření okna:
                List<int> data = new List<int>();
                data.Add(this._FunctionListIsCategorized ? 2 : 1);
                data.Add(this._VariableListIsCategorized ? 2 : 1);
                data.Add(this._Split.SplitterPosition);
                return data.ToArray();
            }
        }
        #endregion
        #region Public property a eventy
        /// <summary>
        /// Kontext, v kterém se vyhodnocuje výraz. Obsahuje a vrací proměnné.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEvaluateContext Context
        {
            get { return this._Context; }
            set { this._Context = value; this._VariableListFill(); }
        }
        private IEvaluateContext _Context;
        /// <summary>
        /// Hodnota výrazu, aktuální
        /// </summary>
        [Browsable(true)]
        [Category("Formula")]
        [Description("Hodnota výrazu, aktuální")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string Expression
        {
            get { return this._FormulaEditor.EditedText; }
            set
            {
                this._FormulaEditor.EditedText = value;
                this._FormulaEditor.RtfTextBox.ClearUndo();
                this._PanelOk.IsDirty = false;
                this._ToolStripRefresh();
            }
        }
        /// <summary>
        /// Událost, kdy uživatel změnil text (každé jednotlivé písmeno).
        /// Text se nachází v property this.Expression
        /// </summary>
        [Browsable(true)]
        [Category("Formula")]
        [Description("Událost, kdy uživatel změnil text (každé jednotlivé písmeno).")]
        public event EventHandler ExpressionChanged;
        /// <summary>
        /// Událost, kdy uživatel chce uložit obsah editovaného textu.
        /// Text se nachází v property this.Expression
        /// </summary>
        [Browsable(true)]
        [Category("Formula")]
        [Description("Událost, kdy uživatel chce uložit obsah editovaného textu.")]
        public event EventHandler ExpressionSave;
        /// <summary>
        /// Parsované segmenty aktuálního textu. Po každé změně textu jsou aktuální.
        /// Typicky je může přejímat handler a reagovat na ně.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEnumerable<Asol.Reporting.Support.Parser.ParserSegment> Segments { get { return this._FormulaEditor.EditorSegments; } }
        #endregion
        #region ToolStrip
        private void _ToolStripInit()
        {
            //this._TbUndo.Image = Pics.IconLibrary16.EditUndo_FromFile;
            //this._TbRedo.Image = Pics.IconLibrary16.EditRedo_FromFile;
            this._TbUndo.Visible = false;
            this._TbRedo.Visible = false;
            this._TbSep1.Visible = false;
        }
        private void _ToolStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (Object.ReferenceEquals(e.ClickedItem, this._TbUndo))
                this._ToolStrip_Undo();
            else if (Object.ReferenceEquals(e.ClickedItem, this._TbRedo))
                this._ToolStrip_Redo();
            else
                this._ToolStrip_Text(e.ClickedItem.Text);
        }
        private void _ToolStrip_Undo()
        {
            if (this._FormulaEditor.RtfTextBox.CanUndo)
            {
                this._FormulaEditor.RtfTextBox.Undo();
                this._ToolStripRefresh();
            }
        }
        private void _ToolStrip_Redo()
        {
            if (this._FormulaEditor.RtfTextBox.CanRedo)
            {
                this._FormulaEditor.RtfTextBox.Redo();
                this._ToolStripRefresh();
            }
        }
        private void _ToolStrip_Text(string text)
        {
            if (text == "(..)")
            {
                this._FormulaEditor.SelectedText = "(" + this._FormulaEditor.SelectedText + ")";
                this._FormulaEditor.SelectRange(this._FormulaEditor.SelectionStart + 1, this._FormulaEditor.SelectionLength - 2);
            }
            else
            {
                this._FormulaEditor.SelectedText = text;
                this._FormulaEditor.SelectRange(this._FormulaEditor.SelectionStart + this._FormulaEditor.SelectionLength, 0);
            }
            this._ToolStripRefresh();
        }
        private void _ToolStripRefresh()
        {
            this._TbUndo.Enabled = this._FormulaEditor.RtfTextBox.CanUndo;
            this._TbRedo.Enabled = this._FormulaEditor.RtfTextBox.CanRedo;
        }
        #endregion
        #region Editor
        private void _EditorInit()
        {
            this._FormulaEditor.EditedTextChanged += new EventHandler(_FormulaEditor_EditedTextChanged);
        }
        private void _FormulaEditor_EditedTextChanged(object sender, EventArgs e)
        {
            this._PanelOk.IsDirty = true;
            this._StatusBarProgress.MainText = "Změna výrazu...";
            this._StatusBarProgress.MainTextColorMorph = new Asol.Reporting.Support.ColorMorph(Color.LightGreen, 0.35f);
            this.OnExpressionChanged();
        }
        protected virtual void OnExpressionChanged()
        {
            if (this.ExpressionChanged != null)
                this.ExpressionChanged(this, EventArgs.Empty);
        }
        #endregion
        #region Seznam funkcí
        /// <summary>
        /// Iniciace listu: vlastnosti, eventhandlery
        /// </summary>
        private void _FunctionListInit()
        {
            this._VariableList.Columns.Clear();
            this._VariableList.Columns.Add(new ColumnHeader() { Text = "Function", Width = 140, TextAlign = HorizontalAlignment.Left });

            this._FunctionList.FullRowSelect = true;
            this._FunctionList.MultiSelect = false;
            this._FunctionList.HideSelection = false;
            this._FunctionList.View = View.Details;
            this._FunctionList.ShowItemToolTips = true;
            this._FunctionList.MouseClick += new MouseEventHandler(_FunctionList_MouseClick);
            this._FunctionList.SizeChanged += new EventHandler(_FunctionList_SizeChanged);
            this._FunctionListColumnLayout();

            this._FunctionListCategorized.Value = "A";
            this._FunctionListCategorized.ValueChanged += new EventHandler(_FunctionListCategorized_ValueChanged);

            this._FunctionListFill();
        }
        /// <summary>
        /// Po změně velikosti
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _FunctionList_SizeChanged(object sender, EventArgs e)
        {
            this._FunctionListColumnLayout();
        }
        /// <summary>
        /// Upraví šířku sloupců dle šířky prvku
        /// </summary>
        private void _FunctionListColumnLayout()
        {
            if (this._VariableList.Columns.Count == 1)
            {
                int width = this._FunctionList.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth - 8;
                this._FunctionList.Columns[0].Width = width;
            }
        }
        /// <summary>
        /// Po kliknutí myší do seznamu funkcí
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _FunctionList_MouseClick(object sender, MouseEventArgs e)
        {
            string fullName = (this._FunctionList.Items.Count == 0 || this._FunctionList.SelectedItems.Count != 1 ? null : this._FunctionList.SelectedItems[0].Text);
            if (fullName != null)
            {
                this._FormulaEditor.SelectedText = fullName;

                int start = fullName.IndexOf("(") + 1;
                int length = fullName.Length - start - 1;
                start = start + this._FormulaEditor.SelectionStart;
                this._FormulaEditor.SelectRange(start, length);

                this._FormulaEditor.Focus();
            }
        }
        /// <summary>
        /// Po změně grupování
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _FunctionListCategorized_ValueChanged(object sender, EventArgs e)
        {
            this._FunctionListFill();
            this._FunctionList.Focus();
        }
        /// <summary>
        /// Naplní _FunctionList seznamem funkcí.
        /// Akceptuje grupování dle kategorií.
        /// </summary>
        private void _FunctionListFill()
        {
            string value = (this._FunctionList.Items.Count == 0 || this._FunctionList.SelectedItems.Count != 1 ? null : this._FunctionList.SelectedItems[0].Text);
            ListViewItem selectItem = null;
            this._FunctionList.Items.Clear();
            this._FunctionList.Groups.Clear();
            this._FunctionGroups = null;
            
            List<FunctionImplement> functions = Evaluator.FunctionImplementList;
            if (functions == null || functions.Count == 0) return;

            bool byGroup = this._FunctionListIsCategorized;
            if (byGroup)
                this._FunctionListPrepareGroup(functions);

            foreach (FunctionImplement function in functions)
            {
                ListViewItem item = new ListViewItem(function.FullName);
                item.ToolTipText = function.ToolTipText;
                if (byGroup)
                    item.Group = this._FunctionListGetGroup(function.ListGroupName);
                this._FunctionList.Items.Add(item);

                if (value != null && value == function.FullName)
                {
                    item.Selected = true;
                    selectItem = item;
                }
            }
            if (selectItem != null)
                selectItem.EnsureVisible();
        }
        private bool _FunctionListIsCategorized
        { 
            get { return (this._FunctionListCategorized.Value.CompareTo("C") == 0); }
            set { this._FunctionListCategorized.Value = (value ? "C" : "A"); }
        }
        /// <summary>
        /// Připraví Groups do _FunctionList
        /// </summary>
        /// <param name="functions"></param>
        private void _FunctionListPrepareGroup(List<FunctionImplement> functions)
        {
            List<string> groups = functions
                .ConvertAll<string>(f => f.ListGroupName)
                .Where(g => !String.IsNullOrEmpty(g))
                .Distinct()
                .ToList();
            groups.Sort();
            this._FunctionGroups = new Dictionary<string, ListViewGroup>();
            foreach (string group in groups)
            {
                ListViewGroup newGroup = new ListViewGroup(group, HorizontalAlignment.Left);
                newGroup.Name = group;
                this._FunctionList.Groups.Add(newGroup);
                this._FunctionGroups.Add(group, newGroup);
            }
        }
        /// <summary>
        /// Vrátí Group daného jména pro _FunctionList.
        /// </summary>
        /// <param name="groupName"></param>
        /// <returns></returns>
        private ListViewGroup _FunctionListGetGroup(string groupName)
        {
            if (String.IsNullOrEmpty(groupName)) return null;
            if (this._FunctionGroups == null) return null;
            ListViewGroup newGroup;
            if (this._FunctionGroups.TryGetValue(groupName, out newGroup))
                return newGroup;
            return null;
        }
        /// <summary>
        /// Index objektů FunctionGroup
        /// </summary>
        private Dictionary<string, ListViewGroup> _FunctionGroups;
        #endregion
        #region Seznam proměnných
        /// <summary>
        /// Iniciace listu: vlastnosti, eventhandlery
        /// </summary>
        private void _VariableListInit()
        {
            this._VariableList.Columns.Clear();
            this._VariableList.Columns.Add(new ColumnHeader() { Text = "Variable name", Width = 140, TextAlign = HorizontalAlignment.Left });
            this._VariableList.Columns.Add(new ColumnHeader() { Text = "Type", Width = 110, TextAlign = HorizontalAlignment.Left });

            this._VariableList.FullRowSelect = true;
            this._VariableList.MultiSelect = false;
            this._VariableList.HideSelection = false;
            this._VariableList.View = View.Details;
            this._VariableList.ShowItemToolTips = true;
            this._VariableList.MouseClick += new MouseEventHandler(_VariableList_MouseClick);
            this._VariableList.SizeChanged += new EventHandler(_VariableList_SizeChanged);
            this._VariableListColumnLayout();

            this._VariableListCategorized.Value = "A";
            this._VariableListCategorized.ValueChanged += new EventHandler(_VariableListCategorized_ValueChanged);

            this._VariableListFill();
        }
        /// <summary>
        /// Po změně velikosti
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _VariableList_SizeChanged(object sender, EventArgs e)
        {
            this._VariableListColumnLayout();
        }
        /// <summary>
        /// Upraví šířku sloupců dle šířky prvku
        /// </summary>
        private void _VariableListColumnLayout()
        {
            if (this._VariableList.Columns.Count == 2)
            {
                int width = this._VariableList.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth - 8;
                this._VariableList.Columns[0].Width = width * 65 / 100;
                this._VariableList.Columns[1].Width = width * 35 / 100;
            }
        }
        /// <summary>
        /// Po kliknutí myší do seznamu proměnných
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _VariableList_MouseClick(object sender, MouseEventArgs e)
        {
            string variableName = (this._VariableList.Items.Count == 0 || this._VariableList.SelectedItems.Count != 1 ? null : this._VariableList.SelectedItems[0].Text);
            if (variableName != null)
            {
                this._FormulaEditor.SelectedText = variableName;
                this._FormulaEditor.SelectRange(this._FormulaEditor.SelectionStart + this._FormulaEditor.SelectionLength, 0);
                this._FormulaEditor.Focus();
            }
        }
        /// <summary>
        /// Po změně grupování v seznamu proměnných
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _VariableListCategorized_ValueChanged(object sender, EventArgs e)
        {
            this._VariableListFill();
            this._VariableList.Focus();
        }
        /// <summary>
        /// Naplní _VariableList seznamem funkcí.
        /// Akceptuje grupování dle kategorií.
        /// </summary>
        private void _VariableListFill()
        {
            string value = (this._VariableList.Items.Count == 0 || this._VariableList.SelectedItems.Count != 1 ? null : this._VariableList.SelectedItems[0].Text);
            ListViewItem selectItem = null;
            this._VariableList.Items.Clear();
            this._VariableList.Groups.Clear();
            this._VariableGroups = null;

            if (this._Context == null) return;
            IEnumerable<VariableInfo> variables = this._Context.AllVariableInfo;
            if (variables == null) return;

            bool byGroup = this._VariableListIsCategorized;
            if (byGroup)
                this._VariableListPrepareGroup(variables);

            foreach (VariableInfo variable in variables)
            {
                ListViewItem item = new ListViewItem(variable.Name);
                item.SubItems.Add(variable.TypeName);
                item.ToolTipText = variable.ToolTipText;
                if (byGroup)
                    item.Group = this._VariableListGetGroup(variable.ListGroupName);
                this._VariableList.Items.Add(item);

                if (value != null && value == variable.Name)
                {
                    item.Selected = true;
                    selectItem = item;
                }
            }
            if (selectItem != null)
                selectItem.EnsureVisible();
        }
        private bool _VariableListIsCategorized
        {
            get { return (this._VariableListCategorized.Value.CompareTo("C") == 0); }
            set { this._VariableListCategorized.Value = (value ? "C" : "A"); }
        }
        /// <summary>
        /// Připraví Groups do _VariableList
        /// </summary>
        /// <param name="variables"></param>
        private void _VariableListPrepareGroup(IEnumerable<VariableInfo> variables)
        {
            List<string> groups = variables
                .ToList()
                .ConvertAll<string>(f => f.ListGroupName)
                .Where(g => !String.IsNullOrEmpty(g))
                .Distinct()
                .ToList();
            groups.Sort();
            this._VariableGroups = new Dictionary<string, ListViewGroup>();
            foreach (string group in groups)
            {
                ListViewGroup newGroup = new ListViewGroup(group, HorizontalAlignment.Left);
                newGroup.Name = group;
                this._VariableList.Groups.Add(newGroup);
                this._VariableGroups.Add(group, newGroup);
            }
        }
        /// <summary>
        /// Vrátí Group daného jména pro _VariableList.
        /// </summary>
        /// <param name="groupName"></param>
        /// <returns></returns>
        private ListViewGroup _VariableListGetGroup(string groupName)
        {
            if (String.IsNullOrEmpty(groupName)) return null;
            if (this._VariableGroups == null) return null;
            ListViewGroup newGroup;
            if (this._VariableGroups.TryGetValue(groupName, out newGroup))
                return newGroup;
            return null;
        }
        /// <summary>
        /// Index objektů VariableGroup
        /// </summary>
        private Dictionary<string, ListViewGroup> _VariableGroups;
        #endregion
        #region PanelOK
        private void _PanelOKInit()
        {
            this._PanelOk.DirtySensitive = true;
            this._PanelOk.IsDirty = false;
            this._PanelOk.Save += new EventHandler(_PanelOk_Save);
        }
        void _PanelOk_Save(object sender, EventArgs e)
        {
            this.OnExpressionSave();
            this._PanelOk.IsDirty = false;
        }
        protected virtual void OnExpressionSave()
        {
            if (this.ExpressionSave != null)
                this.ExpressionSave(this, EventArgs.Empty);
        }
        #endregion
    }
}
